home *** CD-ROM | disk | FTP | other *** search
- /*
- ** Accountant.c
- **
- ** Connection cost accounting
- **
- ** Copyright © 1990-1996 by Olaf `Olsen' Barthel
- ** All Rights Reserved
- **
- ** :ts=4
- */
-
- #ifndef _GLOBAL_H
- #include "Global.h"
- #endif
-
- /****************************************************************************/
-
- enum { STATE_Idle, STATE_Waiting };
-
- /****************************************************************************/
-
- enum { ACMSG_Start,ACMSG_Stop,ACMSG_Query };
-
- typedef struct AccountantMsg
- {
- struct Message Message;
-
- UWORD Type;
- struct timeval Time;
- } AccountantMsg;
-
- /****************************************************************************/
-
- STATIC struct MsgPort *AccountantPort;
- STATIC struct Task *AccountantTask;
-
- STATIC struct SignalSemaphore AccountantSemaphore;
- STATIC ULONG AccountantCost;
-
- /****************************************************************************/
-
- /* AccountantEntry(VOID):
- *
- * The rates accounting task.
- */
-
- STATIC VOID __saveds __asm
- AccountantEntry(VOID)
- {
- struct MsgPort *TimePort;
-
- // Set up the timer resources
-
- if(TimePort = CreateMsgPort())
- {
- struct timerequest *TimeRequest;
-
- if(TimeRequest = (struct timerequest *)CreateIORequest(TimePort,sizeof(struct timerequest)))
- {
- if(!OpenDevice(TIMERNAME,UNIT_VBLANK,TimeRequest,NULL))
- {
- struct Device *TimerBase;
-
- TimerBase = TimeRequest->tr_node.io_Device;
-
- // Now create the communications port
-
- if(AccountantPort = CreateMsgPort())
- {
- struct timeval StopTime;
- struct timeval Now;
- ULONG Signals;
- WORD State;
-
- // That's me
-
- AccountantTask = FindTask(NULL);
-
- // Wake up the main program
-
- Signal(ThisProcess,SIG_HANDSHAKE);
-
- // Doing nothing yet...
-
- State = STATE_Idle;
-
- FOREVER
- {
- // Wait for a sign
-
- Signals = Wait(PORTMASK(TimePort) | PORTMASK(AccountantPort) | SIG_KILL);
-
- // Terminate this task?
-
- if(Signals & SIG_KILL)
- break;
-
- // The timer has elapsed?
-
- if((Signals & PORTMASK(TimePort)) && State == STATE_Waiting)
- {
- ULONG Cost;
-
- // Terminate the timer request
-
- WaitIO(TimeRequest);
-
- // We are no longer waiting
-
- State = STATE_Idle;
-
- // Start with a clean slate
-
- Cost = 0;
-
- // We want to read the accounting data
-
- ObtainSemaphoreShared(&PatternSemaphore);
-
- if(ChosenEntry || ChosenPattern)
- {
- // Look up the current time
-
- GetSysTime(&Now);
-
- // Check which rate data is current
-
- SelectTime(ChosenEntry,ChosenPattern,&Now);
-
- // Store the cost for the next unit
-
- Cost = PayPerUnit[DT_NEXT_UNIT];
-
- // Does this unit last for a certain time?
-
- if(SecPerUnit[DT_NEXT_UNIT] > 0)
- {
- struct timeval TimeVal;
-
- // This is the time it takes for the
- // unit to elapse
-
- TimeVal.tv_secs = SecPerUnit[DT_NEXT_UNIT] / 10000;
- TimeVal.tv_micro = (SecPerUnit[DT_NEXT_UNIT] % 10000) * 100;
-
- // Remember when the unit will have elapsed
-
- GetSysTime(&StopTime);
- AddTime(&StopTime,&TimeVal);
-
- // Start waiting again
-
- TimeRequest->tr_node.io_Command = TR_ADDREQUEST;
- TimeRequest->tr_time = TimeVal;
-
- SendIO(TimeRequest);
-
- State = STATE_Waiting;
- }
- }
-
- ReleaseSemaphore(&PatternSemaphore);
-
- // Add up the connection cost
-
- ObtainSemaphore(&AccountantSemaphore);
- AccountantCost += Cost;
- ReleaseSemaphore(&AccountantSemaphore);
- }
-
- // Somebody wants us to do something...
-
- if(Signals & PORTMASK(AccountantPort))
- {
- AccountantMsg *Message;
-
- // For each message...
-
- while(Message = (AccountantMsg *)GetMsg(AccountantPort))
- {
- switch(Message->Type)
- {
- // Start waiting?
-
- case ACMSG_Start:
-
- // Remember the time when this unit
- // will have elapsed
-
- GetSysTime(&StopTime);
- AddTime(&StopTime,&Message->Time);
-
- // Start waiting
-
- TimeRequest->tr_node.io_Command = TR_ADDREQUEST;
- TimeRequest->tr_time = Message->Time;
-
- SendIO(TimeRequest);
-
- State = STATE_Waiting;
-
- break;
-
- // Stop waiting?
-
- case ACMSG_Stop:
-
- // Are we currently waiting?
-
- if(State == STATE_Waiting)
- {
- if(!CheckIO(TimeRequest))
- AbortIO(TimeRequest);
-
- WaitIO(TimeRequest);
-
- State = STATE_Idle;
- }
-
- break;
-
- // Return the time that still has to elapse?
-
- case ACMSG_Query:
-
- if(State == STATE_Waiting)
- {
- // This is now
-
- GetSysTime(&Now);
-
- // Is there still time left
- // before the unit elapses?
-
- if(CmpTime(&StopTime,&Now) < 0) // i.e. "StopTime > Now"
- {
- // Return the remaining time
-
- Message->Time = StopTime;
-
- SubTime(&Message->Time,&Now);
-
- break;
- }
- }
-
- // Clear the time value
-
- memset(&Message->Time,0,sizeof(Message->Time));
-
- break;
- }
-
- // Return the message
-
- ReplyMsg(Message);
- }
- }
- }
-
- // Are we still waiting?
-
- if(State == STATE_Waiting)
- {
- if(!CheckIO(TimeRequest))
- AbortIO(TimeRequest);
-
- WaitIO(TimeRequest);
- }
-
- // Start cleaning up...
-
- DeleteMsgPort(AccountantPort);
- }
-
- CloseDevice(TimeRequest);
- }
-
- DeleteIORequest(TimeRequest);
- }
-
- DeleteMsgPort(TimePort);
- }
-
- // Finished; wave goodbye and exit
-
- Forbid();
-
- AccountantTask = NULL;
- Signal(ThisProcess,SIG_HANDSHAKE);
- }
-
- /****************************************************************************/
-
- /* DeleteAccountant():
- *
- * Stop the accounting task.
- */
-
- VOID
- DeleteAccountant()
- {
- if(AccountantTask)
- {
- Forbid();
-
- SetSignal(0,SIG_HANDSHAKE);
- Signal(AccountantTask,SIG_KILL);
-
- Permit();
- }
- }
-
- /* CreateAccountant():
- *
- * Launch the accounting task.
- */
-
- BOOL
- CreateAccountant()
- {
- InitSemaphore(&AccountantSemaphore);
-
- Forbid();
-
- if(CreateTask("term Accountant Task",MIN(127,ThisProcess->pr_Task.tc_Node.ln_Pri + 10),AccountantEntry,4096))
- {
- SetSignal(0,SIG_HANDSHAKE);
-
- Wait(SIG_HANDSHAKE);
- }
-
- Permit();
-
- return((BOOL)(AccountantTask != NULL));
- }
-
- /****************************************************************************/
-
- /* QueryAccountantTime(struct timeval *Time):
- *
- * Query the time that will have to elapse before
- * the next unit will begin.
- */
-
- ULONG
- QueryAccountantTime(struct timeval *Time)
- {
- AccountantMsg Message;
-
- memset(&Message,0,sizeof(Message));
-
- Message.Message.mn_Length = sizeof(Message);
- Message.Type = ACMSG_Query;
-
- SendMessageGetReply(AccountantPort,&Message);
-
- if(Time)
- {
- Time->tv_secs = Message.Time.tv_secs;
- Time->tv_micro = Message.Time.tv_micro;
- }
-
- return(Message.Time.tv_secs);
- }
-
- /* QueryAccountantCost():
- *
- * Query the cost of the current connection.
- */
-
- ULONG
- QueryAccountantCost()
- {
- ULONG Cost;
-
- ObtainSemaphoreShared(&AccountantSemaphore);
- Cost = AccountantCost;
- ReleaseSemaphore(&AccountantSemaphore);
-
- return(Cost);
- }
-
- /* StopAccountant():
- *
- * Stop cost accounting and return the current
- * connection cost.
- */
-
- ULONG
- StopAccountant()
- {
- AccountantMsg Message;
- ULONG Cost;
-
- memset(&Message,0,sizeof(Message));
-
- Message.Message.mn_Length = sizeof(Message);
- Message.Type = ACMSG_Stop;
-
- SendMessageGetReply(AccountantPort,&Message);
-
- ObtainSemaphoreShared(&AccountantSemaphore);
- Cost = AccountantCost;
- ReleaseSemaphore(&AccountantSemaphore);
-
- return(Cost);
- }
-
- /* StartAccountant(ULONG OnlineSeconds):
- *
- * Start cost accounting for the current connection.
- */
-
- VOID
- StartAccountant(ULONG OnlineSeconds)
- {
- struct timeval Remain;
- struct timeval Now;
- ULONG Cost;
-
- // Forget the old data
-
- StopAccountant();
-
- // Now is the time for all good men...
-
- GetSysTime(&Now);
-
- // Did the connection happen a few seconds earlier?
-
- if(OnlineSeconds > 0)
- {
- struct timeval Lead;
-
- // Take care of the leading time
-
- Lead.tv_secs = OnlineSeconds;
- Lead.tv_micro = 0;
-
- // Technically, the connection happened a bit earlier.
-
- SubTime(&Now,&Lead);
-
- // Choose the right rate accounting time.
-
- SelectTime(ChosenEntry,ChosenPattern,&Now);
-
- // Enter the first rate.
-
- Cost = PayPerUnit[DT_FIRST_UNIT];
-
- Remain.tv_secs = SecPerUnit[DT_FIRST_UNIT] / 10000;
- Remain.tv_micro = (SecPerUnit[DT_FIRST_UNIT] % 10000) * 100;
-
- // Did the first unit elapse already?
-
- while(CmpTime(&Lead,&Remain) <= 0) // i.e. "Lead >= Remain"
- {
- // Subtract the unit time
-
- SubTime(&Lead,&Remain);
-
- // Update the "current" time so SelectTime
- // can do something sensible.
-
- AddTime(&Now,&Remain);
-
- SelectTime(ChosenEntry,ChosenPattern,&Now);
-
- Remain.tv_secs = SecPerUnit[DT_NEXT_UNIT] / 10000;
- Remain.tv_micro = (SecPerUnit[DT_NEXT_UNIT] % 10000) * 100;
-
- // In any case, a new unit starts here
-
- Cost += PayPerUnit[DT_NEXT_UNIT];
- }
-
- // Adjust the remaining time it takes for the unit
- // to elapse.
-
- SubTime(&Remain,&Lead);
- }
- else
- {
- // Choose the right rate accounting time.
-
- SelectTime(ChosenEntry,ChosenPattern,&Now);
-
- // Enter the first rate.
-
- Cost = PayPerUnit[DT_FIRST_UNIT];
-
- Remain.tv_secs = SecPerUnit[DT_FIRST_UNIT] / 10000;
- Remain.tv_micro = (SecPerUnit[DT_FIRST_UNIT] % 10000) * 100;
- }
-
- // Store the cost
-
- ObtainSemaphore(&AccountantSemaphore);
- AccountantCost = Cost;
- ReleaseSemaphore(&AccountantSemaphore);
-
- // That's it; "Remain" now holds the time to wait before
- // the first unit elapses. Careful here, we must not
- // launch a `zero' time wait request.
-
- if(Remain.tv_secs > 0 || Remain.tv_micro > 0)
- {
- AccountantMsg Message;
-
- memset(&Message,0,sizeof(Message));
-
- Message.Message.mn_Length = sizeof(Message);
- Message.Type = ACMSG_Start;
- Message.Time = Remain;
-
- SendMessageGetReply(AccountantPort,&Message);
- }
- }
-